﻿using System;
using System.Security.Cryptography;

namespace Framework.Crypto.Hashers
{
    /// <summary>
    /// Generic hash algorythm abstraction.
    /// Implements ICryptoProvider.
    /// </summary>
    public abstract class IHasher : ICryptoProvider // We'll also create IDisposable inheritance later here or to childs.
    {
        /*
         may be use generic HashAlgorythm field as a basis ?
         */

        /// <summary>
        /// Content to be hashed
        /// </summary>
        protected byte[] signer;

        /// <summary>
        /// Gets the content to be hashed
        /// </summary>
        public byte[] Signer { get { return signer; } }

        /// <summary>
        /// Key for key-based hashing
        /// </summary>
        protected byte[] key;

        /// <summary>
        /// Gets the key for key-based hashing
        /// </summary>
        public byte[] Key { get { return key; } }

        /// <summary>
        /// Key-based flag
        /// </summary>
        public readonly bool keybased = false;

        /// <summary>
        /// Basic constructor for non key-based hash algorythms.
        /// </summary>
        /// <param name="signer">content to be hashed</param>
        public IHasher(byte[] signer)
        {
            this.signer = signer;
        }

        /// <summary>
        /// Basic constructor for key-based hash algorythms.
        /// </summary>
        /// <param name="signer">content to be hashed</param>
        /// <param name="key">key for hashing algorythm</param>
        public IHasher(byte[] signer, byte[] key)
        {
            if (key == null)
            {
                throw new ArgumentNullException("key", "You're trying to use null byte[] array as a key.");
            }

            this.keybased = true;
            this.signer = signer;
            this.key = key;
        }

        /// <summary>
        /// Keygen constructor for key-based hash algorythms.
        /// </summary>
        /// <param name="signer">content to be hashed</param>
        /// <param name="key">key for hashing algorythm</param>
        public IHasher(byte[] signer, int keyLength)
        {
            this.keybased = true;
            this.signer = signer;
            Keygen(keyLength);
        }

        /// <summary>
        /// Keygen algorythm
        /// </summary>
        public virtual void Keygen(int keyLength)
        {
            if(keybased)
            {
                // lets use RNGCrypto for more security
                byte[] newKey = new byte[keyLength];
                //RNGCryptoServiceProvider is an implementation of a random number generator.
                RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
                // The array is now filled with cryptographically strong random bytes.
                rng.GetBytes(newKey);
                key = newKey;
            }
            else
                throw new MethodAccessException("This is not a key-based algorythm.");
        }

        /// <summary>
        /// Appends byte[]-based content to buffer.
        /// </summary>
        /// <param name="appendix"></param>
        public void Append(byte[] appendix)
        {
            if(appendix == null) 
            {
                throw new ArgumentNullException("appendix","You're trying to append null byte[] array.");
            }

            int newSize = signer.Length + appendix.Length;
            byte[] newSigner = new byte[newSize];
            Array.Copy(signer, newSigner, signer.Length);
            Array.Copy(appendix, 0, newSigner, signer.Length, appendix.Length);
            signer = newSigner;
        }

        /// <summary>
        /// Value verifier.
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public bool Verify(byte[] value)
        {
            return value.Equals(Sign());
        }

        /// <summary>
        /// Returns computed hash byte array.
        /// </summary>
        /// <returns>signed byte[]</returns>
        public abstract byte[] Sign();

        /// <summary>
        /// Returns a string-based hash representation.
        /// </summary>
        public abstract override string ToString();

        /// <summary>
        /// Clearance of byte buffer.
        /// </summary>
        public abstract void Clear();
    }
}
